package sparrow.common;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.LinkedList;
import java.util.List;

import org.jivesoftware.smack.Chat;
import org.jivesoftware.smack.ChatManager;
import org.jivesoftware.smack.ConnectionConfiguration;
import org.jivesoftware.smack.MessageListener;
import org.jivesoftware.smack.Roster;
import org.jivesoftware.smack.RosterEntry;
import org.jivesoftware.smack.RosterListener;
import org.jivesoftware.smack.SASLAuthentication;
import org.jivesoftware.smack.XMPPConnection;
import org.jivesoftware.smack.XMPPException;
import org.jivesoftware.smack.packet.Message;
import org.jivesoftware.smack.packet.Presence;

public class Client {

	// User info
	private String loginName;
	private char[] password;
	private boolean isOnline;

	// Chat logs
	private TopicHistory topics;
	private List<Chat> chatSessions;

	// Network info
	private XMPPConnection connection;
	private ChatManager chatManager;
	private Roster buddyList;

	// GUI Helper
	/* XXX What to do when user receives a msg. */
	private MessageListener receiveListener;
	/* XXX What to do when buddies changes status. */
	private RosterListener buddyStatusListener;

	/* Set up default listeners for receiving messages and updating buddylist */
	public Client(String login, String pass) {
		loginName = login;
		password = pass.toCharArray();
		isOnline = false;
		//topics = new TopicHistory(login);
		chatSessions = new LinkedList<Chat>();
		connection = null;
		chatManager = null;

		receiveListener = new MessageListener() {
			public void processMessage(Chat chat, Message message) {
				System.out.println(chat.getParticipant() + " sends to "
						+ getName());
				System.out.println("Received message: " + message.getBody());
			}
		};

		buddyStatusListener = new RosterListener() {
			public void entriesAdded(Collection<String> addresses) {
			}

			public void entriesDeleted(Collection<String> addresses) {
			}

			public void entriesUpdated(Collection<String> addresses) {
			}

			public void presenceChanged(Presence presence) {
				System.out.println("Presence changed: " + presence.getFrom()
						+ " " + presence);
			}
		};
	}

	/*
	 * This will store password in the client object => security issue Caller
	 * should call login right after, which will clear the password field
	 * listener implements processMessage(), which will be called if this client
	 * receives any message from other clients XXX: GUI => add listener
	 */
	public Client(String login, String pass, MessageListener mListener,
			RosterListener rListener) {
		loginName = login;
		password = pass.toCharArray();
		isOnline = false;
		//topics = new TopicHistory(login);
		chatSessions = new LinkedList<Chat>();
		connection = null;
		chatManager = null;
		receiveListener = mListener;
		buddyStatusListener = rListener;
	}

	public String getName() {
		return loginName;
	}

	public boolean loginTo(String server, int port) {
		if (isOnline)
			return false;
		ConnectionConfiguration config = new ConnectionConfiguration(server,
				port);
		connection = new XMPPConnection(config);
		try {
			connection.connect();
		} catch (XMPPException e) {
			System.out.println("Cannot login");
			e.printStackTrace();
			return false;
		}

		try {
			SASLAuthentication.unsupportSASLMechanism("PLAIN");
			SASLAuthentication.unsupportSASLMechanism("DIGEST-MD5");
			SASLAuthentication.unsupportSASLMechanism("CRAM-MD5");
			connection.login(loginName, password.toString());
			chatManager = connection.getChatManager();
			buddyList = connection.getRoster();
			isOnline = true;
		} catch (XMPPException e) {
			chatManager = null;
			isOnline = false;
			e.printStackTrace();
			return false;
		} finally {
			Arrays.fill(password, '0');
		}
		return true;
	}

	/* Login using default port 5222 */
	public void loginTo(String server) {
		loginTo(server, 5222);
	}

	public void disconnect() {
		if (isOnline) {
			connection.disconnect();
		}
	}

	public void addBuddy(String buddyName) {
		System.out.println(buddyName);
		try {
			buddyList.createEntry(buddyName, buddyName, null);
		} catch (XMPPException e) {
			System.out.println("Can't add buddy");
			e.printStackTrace();
		}
		buddyList.reload();
	}

	/* Return only online buddies for now */
	public ArrayList<RosterEntry> getBuddyList() {
		buddyList = connection.getRoster();
		if (buddyStatusListener != null) // Necessary??
			buddyList.addRosterListener(buddyStatusListener);

		Collection<RosterEntry> buddies = buddyList.getEntries();
		ArrayList<RosterEntry> onlineBuddies = new ArrayList<RosterEntry>();
		Presence pres = null;

		for (RosterEntry buddy : buddies) {
			pres = buddyList.getPresence(buddy.getName());
			if (pres != null) // User is not offline (online)
				onlineBuddies.add(buddy);
		}
		return onlineBuddies;
	}

	public void sendMessage(String buddy, String msg) {
		if (isOnline) {
			//chatManager = connection.getChatManager();
			Chat chatSession = chatManager.createChat(buddy, receiveListener);
			try {
				chatSession.sendMessage(msg);
			} catch (XMPPException e) {
				System.out.println("Cannot send message");
			}
		} else {
			System.out.println("Can't send message while offline");
		}
	}

	public static void main(String[] args) {
		Client vu = new Client("vu", "123456");
		Client john = new Client("john", "123456");

		vu.loginTo("localhost", 5222);
		john.loginTo("localhost", 5222);

		//vu.addBuddy(john.getName());
		//john.addBuddy(vu.getName());
		
		john.sendMessage(vu.getName(), "Hello!!!");
		
		vu.sendMessage(john.getName(), "Hello");
	}

}
